package solver

func (g *Grid) clearupLineInPointing(value uint8, line House, toSkippedCellNum int) bool {
	result := false
	for i, cell := range line.cells {
		if i >= toSkippedCellNum && i < toSkippedCellNum+3 {
			continue
		}
		if cell.value != 0 {
			continue
		}
		if cell.removeCandidate(value) {
			result = true
		}
	}
	return result
}

func (g *Grid) calcCanidateFreq(boxNum int) (rowFreqs [9]int, candidateRows [9][3]bool, colFreqs [9]int, candidateCols [9][3]bool) {
	box := g.boxes[boxNum]
	startRowNum, startColNum := calcRowAndColNum(boxNum, 0)

	for cellNum := 0; cellNum < 9; cellNum++ {
		if box.cells[cellNum].value != 0 {
			continue
		}
		rowNum, colNum := calcRowAndColNum(boxNum, cellNum)
		for i := 0; i < 9; i++ {
			if box.cells[cellNum].candidates[i] {
				candidateRows[i][rowNum-startRowNum] = true
				candidateCols[i][colNum-startColNum] = true
			}
		}
	}

	for i := 0; i < 9; i++ {
		for j := 0; j < 3; j++ {
			if candidateRows[i][j] {
				rowFreqs[i] += 1
			}
			if candidateCols[i][j] {
				colFreqs[i] += 1
			}
		}
	}

	return
}

func (g *Grid) PointingBox(boxNum int) (value uint8, rowNum int, colNum int) {
	rowFreqs, candidateRows, colFreqs, candidateCols := g.calcCanidateFreq(boxNum)

	startRowNum, startColNum := calcRowAndColNum(boxNum, 0)

	for i := 0; i < 9; i++ {
		if rowFreqs[i] == 1 {
			for j := 0; j < 3; j++ {
				if candidateRows[i][j] {
					rowNum = j + startRowNum
					colNum = startColNum
					if g.clearupLineInPointing(uint8(i)+1, g.rows[rowNum], colNum) {
						value = uint8(i) + 1
					}
				}
			}
		}
	}

	for i := 0; i < 9; i++ {
		if colFreqs[i] == 1 {
			for j := 0; j < 3; j++ {
				if candidateCols[i][j] {
					rowNum = startRowNum
					colNum = j + startColNum
					if g.clearupLineInPointing(uint8(i)+1, g.cols[colNum], rowNum) {
						value = uint8(i) + 1
					}
				}
			}
		}
	}

	return
}

func (g *Grid) Pointing() (used bool) {
	for boxNum := 0; boxNum < 9; boxNum++ {
		value, _, _ := g.PointingBox(boxNum)
		if value != 0 {
			return true
		}
	}
	return false
}

func (g *Grid) clearupBoxInClaimingButRow(value uint8, unit *House, skippedRowNum int) bool {
	//fmt.Println("skiplinenum:", skippedLineNum, value)
	result := false
	for i, cell := range unit.cells {
		if i/3 == skippedRowNum%3 {
			continue
		}
		if cell.value != 0 {
			continue
		}
		if cell.removeCandidate(value) {
			result = true
		}
	}
	return result

}

func (g *Grid) clearupBoxInClaimingButCol(value uint8, unit *House, skippedColNum int) bool {
	//fmt.Println("skiplinenum:", skippedLineNum, value)
	result := false
	for i, cell := range unit.cells {
		if i%3 == skippedColNum%3 {
			continue
		}
		if cell.value != 0 {
			continue
		}
		if cell.removeCandidate(value) {
			result = true
		}
	}
	return result

}

func (g *Grid) calcBoxFreqsrow(u *House) (boxFreqs [9]int, candidateBoxes [9][3]bool) {
	for i, cell := range u.cells {
		if cell.value != 0 {
			continue
		}
		boxNum := i / 3
		for j, candidate := range cell.candidates {
			if candidate {
				candidateBoxes[j][boxNum] = true
			}
		}
	}

	for i := 0; i < 9; i++ {
		for j := 0; j < 3; j++ {
			if candidateBoxes[i][j] {
				boxFreqs[i] += 1
			}
		}
	}

	return
}

func (g *Grid) Claiming() (used bool) {
	for rowNum, row := range g.rows {
		boxFreqs, candidateBoxes := g.calcBoxFreqsrow(&row)
		startBoxNum, _ := calcBoxAndCellNum(rowNum, 0)
		for i := 0; i < 9; i++ {
			if boxFreqs[i] == 1 {
				for j := 0; j < 3; j++ {
					if candidateBoxes[i][j] {
						boxNum := startBoxNum + j
						value := uint8(i) + 1
						if g.clearupBoxInClaimingButRow(value, &g.boxes[boxNum], rowNum) {
							used = true
						}
					}
				}
			}
		}
	}

	for colNum, col := range g.cols {
		boxFreqs, candidateBoxes := g.calcBoxFreqsrow(&col)
		startBoxNum, _ := calcBoxAndCellNum(0, colNum)

		for i := 0; i < 9; i++ {
			if boxFreqs[i] == 1 {
				for j := 0; j < 3; j++ {
					if candidateBoxes[i][j] {
						boxNum := startBoxNum + j*3
						value := uint8(i) + 1
						if g.clearupBoxInClaimingButCol(value, &g.boxes[boxNum], colNum) {
							used = true
						}
					}
				}
			}
		}
	}

	return
}
